#include "API.h"
#include "../common.h"

API::API() {
	// Obtengo todos los IPCs dado que no sé cuales voy a utilizar
	
	this->semControl = Semaphore();
	this->semControl.getSemaphore((char *) DIRECTORY, ID_SEMCONTROL, 1);
	this->shMem = ControlDataSharedMemory();
	this->shMem.getSharedMemory((char *) DIRECTORY, ID_SHMEM);
	
	this->semDoor = Semaphore();
	this->semDoor.getSemaphore((char *) DIRECTORY, ID_SEMDOOR, 4);

	this->entryDoorQueueInput = DoorInputMessageQueue();
	this->entryDoorQueueInput.getMessageQueue((char *) DIRECTORY, ID_Q_ENTRY_DOOR_INPUT);
	this->entryDoorQueueOutput = DoorOutputMessageQueue();
	this->entryDoorQueueOutput.getMessageQueue((char *) DIRECTORY, ID_Q_ENTRY_DOOR_OUTPUT);
	this->exitDoorQueueInput = DoorInputMessageQueue();
	this->exitDoorQueueInput.getMessageQueue((char *) DIRECTORY, ID_Q_EXIT_DOOR_INPUT);
	this->exitDoorQueueOutput =DoorOutputMessageQueue();
	this->exitDoorQueueOutput.getMessageQueue((char *) DIRECTORY, ID_Q_EXIT_DOOR_OUTPUT);
}

API::~API() {
}
	
//
bool API::entrarAMuseo(long idPersona, int nroPuerta) {
	PersonaInputMsg inputMsg;
	PersonaOutputMsg outputMsg;
	
	inputMsg.mtype = nroPuerta;
	inputMsg.idPersona = idPersona;
	
	sprintf(this->buffer, "Persona N°%ld: Envía mensaje a puerta de entrada N°%d\n", idPersona, nroPuerta);
	write(fileno(stdout), buffer, strlen(buffer));
	
	this->entryDoorQueueInput.enviarMensaje(inputMsg);
	
	if (this->entryDoorQueueOutput.recibirMensaje(idPersona, &outputMsg) == -1) {
		// TODO: Capturar el errno y ver que pasó. Por el momento, se supone que
		// se cerró el museo
		return false;
	}
	
	sprintf(this->buffer, "Persona N°%ld: Recibe mensaje de puerta de entrada N°%d\n", idPersona, nroPuerta);
	write(fileno(stdout), buffer, strlen(buffer));
	
	return true;
}
	
// 
bool API::salirDeMuseo(long idPersona, int nroPuerta) {
	PersonaInputMsg inputMsg;
	PersonaOutputMsg outputMsg;
	
	inputMsg.mtype = nroPuerta;
	inputMsg.idPersona = idPersona;
	
	sprintf(this->buffer, "Persona N°%ld: Envía mensaje a puerta de salida N°%d\n", idPersona, nroPuerta);
	write(fileno(stdout), buffer, strlen(buffer));
	
	this->exitDoorQueueInput.enviarMensaje(inputMsg);
	
	if (this->exitDoorQueueOutput.recibirMensaje(idPersona, &outputMsg) == -1) {
		// TODO: Capturar el errno y ver que pasó. Por el momento, se supone que
		// se cerró el museo
		return false;
	}
	
	sprintf(this->buffer, "Persona N°%ld: Recibe mensaje de puerta de salida N°%d\n", idPersona, nroPuerta);
	write(fileno(stdout), buffer, strlen(buffer));

	return true;
}

bool API::enviarMensajePuertaEntrada(int nroPuerta, long idPersona) {
	PersonaOutputMsg outputMsg;
	
	outputMsg.mtype = idPersona;
	sprintf(buffer, "Persona N°%ld: Puede ingresar al museo\n", idPersona);
	strcpy(outputMsg.msg, buffer);
	
	sprintf(buffer, "Puerta de entrada N°%d: Enviando mensaje a Persona N°%ld\n", nroPuerta, idPersona);
	write(fileno(stdout), buffer, strlen(buffer));
	
	if (this->entryDoorQueueOutput.enviarMensaje(outputMsg) == -1) {
		sprintf(buffer, "Puerta Entrada Error: %s\n", strerror(errno));
		write(fileno(stdout), buffer, strlen(buffer));
		return false;
	}
	
	return true;
}
	
bool API::recibirMensajePuertaEntrada(int nroPuerta, long *idPersona) {
	PersonaInputMsg inputMsg;
	
	if (this->entryDoorQueueInput.recibirMensaje( (long) nroPuerta, &inputMsg) == -1) {
		// TODO: Actuar en función del error
		return false;
	}
	
	(*idPersona) = inputMsg.idPersona;
	return true;
}

bool API::enviarMensajePuertaSalida(int nroPuerta, long idPersona) {
	PersonaOutputMsg outputMsg;
	
	outputMsg.mtype = idPersona;
	sprintf(buffer, "Persona N°%ld: Puede salir del museo\n", idPersona);
	strcpy(outputMsg.msg, buffer);
	
	if (this->exitDoorQueueOutput.enviarMensaje(outputMsg) == -1) {
		// TODO: Ver error y actuar en consecuencia
		return false;
	}
	
	return true;
}
	
bool API::recibirMensajePuertaSalida(int nroPuerta, long *idPersona) {
	PersonaInputMsg inputMsg;
	
	if (this->exitDoorQueueInput.recibirMensaje( (long) nroPuerta, &inputMsg) == -1) {
		// TODO: Actuar en función del error
		return false;
	}
	
	*idPersona = inputMsg.idPersona;
	return true;
}
	
bool API::reservarEspacioParaUnaPersona(int nroPuerta) {
	ControlData aControlData;	
	bool seLogroReservacion = false;
	
	while (seLogroReservacion == false) {
		
		if (this->semControl.wait() == -1) {
			//TODO: Capturar el mensaje de error y ver que sucedió
			return false;
		}
		
		sprintf(buffer, "Puerta de entrada N°%d: Accede a memoria compartida\n", nroPuerta);
		write(fileno(stdout), buffer, strlen(buffer));
	
		this->shMem.read(&aControlData); 
		
		sprintf(buffer, "Puerta de entrada N°%d: Cantidad de Personas en Museo: %d\n", nroPuerta, aControlData.cantPersonas);
		write(fileno(stdout), buffer, strlen(buffer));
	
		if (aControlData.cantPersonas == MUSEO_MAX) {
			// El museo está lleno, el proceso se debe bloquear y marcarse como
			// bloqueado
			aControlData.puertaBloqueada[nroPuerta-1] = true;
			this->shMem.write(&aControlData);
			
			if (this->semControl.signal() == -1) {
				// TODO: Idem anteriores
				return false;
			}
		
			sprintf(buffer, "Puerta de entrada N°%d: Libera a memoria compartida\n", nroPuerta);
			write(fileno(stdout), buffer, strlen(buffer));
				
				
			sprintf(buffer, "Puerta de entrada N°%d: Se bloquea al intentar hacer reserva\n", nroPuerta);
			write(fileno(stdout), buffer, strlen(buffer));
			
			if (this->semDoor.wait(nroPuerta-1) == -1) {
				// TODO: Idem anteriores
				return false;
			}
			
			sprintf(buffer, "Puerta de entrada N°%d: Es liberada\n", nroPuerta);
			write(fileno(stdout), buffer, strlen(buffer));
			
			continue;
		}
		else {
			// La persona puede entrar al museo, reserva el lugar y libera
			// deja la memoria compartida libre para otros procesos
			aControlData.cantPersonas++;		
			this->shMem.write(&aControlData);
			
			if (this->semControl.signal() == -1) {
				// TODO: Idem anteriores
				return false;
			}
			
			sprintf(buffer, "Puerta de entrada N°%d: Reserva espacio para persona\n", nroPuerta);
			write(fileno(stdout), buffer, strlen(buffer));
			sprintf(buffer, "Puerta de entrada N°%d: Libera a memoria compartida\n", nroPuerta);
			write(fileno(stdout), buffer, strlen(buffer));
			
			seLogroReservacion = true;
		}
	}
	
	return true;
}
	
bool API::liberarEspacioDeUnaPersona(int nroPuerta) {
	ControlData aControlData;
	
	// Se accede a la memoria compartida
	if (this->semControl.wait() == -1 ) {
		// TODO: Capturar el error y actuar según el caso
		return false;
	}	
	
	sprintf(buffer, "Puerta de salida N°%d: Accede a memoria compartida\n", nroPuerta);
	write(fileno(stdout), buffer, strlen(buffer));
	
	this->shMem.read(&aControlData);
	
	sprintf(buffer, "Puerta de salida N°%d: Cantidad de Personas en Museo: %d\n", nroPuerta, aControlData.cantPersonas);
	write(fileno(stdout), buffer, strlen(buffer));
	
	aControlData.cantPersonas--;
	
	if (aControlData.cantPersonas == (MUSEO_MAX-1)) {
		// Como el museo estaba lleno, libero a una 
		// persona y a todos los procesos que estaban
		// intentando hacer entrar a una persona	
		
		sprintf(buffer, "Puerta de salida N°%d: El museo estaba lleno. Se libera a puertas bloqueadas\n", nroPuerta);
		write(fileno(stdout), buffer, strlen(buffer));
		
		for (int i = 0; i < CANT_DOORS; i++) {
			if (aControlData.puertaBloqueada[i] == true) {
				aControlData.puertaBloqueada[i] = false;
			
				sprintf(buffer, "Puerta de salida N°%d: Libera a puerta de entrada %d\n", nroPuerta, i+1);
				write(fileno(stdout), buffer, strlen(buffer));
			
				if (this->semDoor.signal(i) == -1) {
					// TODO: Idem anteriores
					return false;
				}
			}
		}
	}
	
	this->shMem.write(&aControlData);
	sprintf(buffer, "Puerta de salida N°%d: Libera a memoria compartida\n", nroPuerta);
	write(fileno(stdout), buffer, strlen(buffer));
	this->semControl.signal();
	return true;
}
